<?php
require_once 'Zend/Log/Writer/Stream.php';
require_once 'Zend/Log.php';

abstract class Concentre_Daemon_Abstract {

	protected $_uid = 0;
	protected $_gid = 0;
	protected $_home = '/';

	protected $_pid = 0;
// TODO we should generate PID filename automatically, e.g. depends on classname
	protected $_pidfile= '/tmp/daemon.pid';

	protected $_ischildren = false;
	protected $_logger;
	
	protected $_foreground = false;

	public function __construct($pidfile = null ,$uid = null, $gid = null) {

		if (isset($pidfile)) {
			$this->_pidfile = $pidfile;
		}

        if (isset($uid)) {
        	$this->_uid = $uid;
        }

        if (isset($gid)) {
            $this->_gid = $gid;
        }

		$this->daemon_tag = "launcher";
		
		$writer = new Zend_Log_Writer_Stream('pcntl.log');
		$this->_logger = new Zend_Log($writer);		
		
		$this->_logger->info('construct');

//		error_reporting(0);
		set_time_limit(0);
		
		$this->init();

 		if (false == $this->_foreground) {
		 	$this->_daemonize();
		}

	}

	public function init() {
		$this->log('init');
	}

	public function releaseHandler() {
		if ($this->_ischildren && file_exists($this->_pidfile)) {
			posix_kill($this->_pid, $this->_pidfile);
		    @unlink($this->_pidfile);
		}

	}

	protected function sighandler($signo) {
		switch ($signo) {
			case SIGTERM:
	            $this->_logger->info("got SIGTERM");
				$this->stop();
				exit( 0 );
				break;
			case SIGHUP:
				$this->_logger->info("restarting process...");
				break;
			default:
		}
	}

    private function _isrunning() {
        $oldPid = @file_get_contents($this->_pidfile);
        return $oldPid !== false && posix_kill(trim($oldPid),0);
    }

	private function _daemonize() {
		if ($this->_isrunning()) {
			$this->_logger->info("Daemon already running ...");
			exit();
		}

		$pid = pcntl_fork();

		if ($pid == -1) {
			$this->_logger->err("could not fork");
			exit();
		} else if ($pid) {
			exit(); // we are the parent
		} else {
 			$this->_ischildren = true;	
			$this->_pid = posix_getpid();
			file_put_contents($this->_pidfile, $this->_pid);
		}

		if (!posix_setgid($this->_gid) || !posix_setuid($this->_uid)) {
        	$this->_logger->err('could not set identity');
        	exit;
        }

		if (!posix_setsid()) {
			$this->_logger->err("could not detach from terminal");
			exit;
		}

		
        declare(ticks = 1);
        pcntl_signal( SIGTERM, array(&$this, "sighandler") );

        @chdir($this->_home);
        umask(0);
		
		register_shutdown_function(array(&$this, 'releaseHandler'));
	}

	public function start() {
		$this->_running = true;

		while ($this->_running) {
			$this->main();
			usleep(1000);
		}

	}

//	TODO 'main' method should be declared abstract, 
//		 because functionality will be implemented in the each daemon separately  
	public function main() {
		$this->stop();
	}

	public function stop() {
		$this->_running = false;
	}

}